SCORE: 0
BEST: 0
0 KM/H
← → Arrow keys or swipe
to switch lanes

NEON RACER

Dodge the oncoming traffic.
Don't crash.
Survive the night highway.

CRASHED!

Score: 0

High Score: 0

const carImg = new Image(); carImg.src = 'your-car-image-url.png'; // Replace with your image path let carLoaded = false; carImg.onload = () => { carLoaded = true; }; // Game state let gameRunning = false; let score = 0; let highScore = localStorage.getItem('neonRacerHigh') || 0; let speed = 0; let maxSpeed = 15; let frame = 0; // 3 Lanes (x positions) const lanes = [70, 200, 330]; let currentLane = 1; // Player car const player = { x: lanes[1], y: 550, width: 50, height: 80, targetX: lanes[1], tilt: 0 }; // Enemy cars let enemies = []; let roadOffset = 0; // Custom car image let customCarImage = null; let imageLoaded = false; // Audio context const audioContext = new (window.AudioContext || window.webkitAudioContext)(); // Engine sound (low rumble) let engineOscillator = null; function startEngineSound() { if (engineOscillator) return; engineOscillator = audioContext.createOscillator(); const gainNode = audioContext.createGain(); engineOscillator.connect(gainNode); gainNode.connect(audioContext.destination); engineOscillator.type = 'sawtooth'; engineOscillator.frequency.setValueAtTime(80, audioContext.currentTime); gainNode.gain.setValueAtTime(0.05, audioContext.currentTime); engineOscillator.start(); } function updateEngineSound() { if (!engineOscillator || !gameRunning) return; const targetFreq = 60 + (speed * 10); engineOscillator.frequency.setTargetAtTime(targetFreq, audioContext.currentTime, 0.1); } function stopEngineSound() { if (engineOscillator) { engineOscillator.stop(); engineOscillator = null; } } // Crash sound function playCrashSound() { const osc = audioContext.createOscillator(); const gain = audioContext.createGain(); osc.connect(gain); gain.connect(audioContext.destination); osc.type = 'sawtooth'; osc.frequency.setValueAtTime(200, audioContext.currentTime); osc.frequency.exponentialRampToValueAtTime(20, audioContext.currentTime + 0.5); gain.gain.setValueAtTime(0.5, audioContext.currentTime); gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5); osc.start(); osc.stop(audioContext.currentTime + 0.5); // Stop engine stopEngineSound(); } // Lane change sound function playWhoosh() { const osc = audioContext.createOscillator(); const gain = audioContext.createGain(); osc.connect(gain); gain.connect(audioContext.destination); osc.type = 'sine'; osc.frequency.setValueAtTime(400, audioContext.currentTime); osc.frequency.exponentialRampToValueAtTime(200, audioContext.currentTime + 0.1); gain.gain.setValueAtTime(0.1, audioContext.currentTime); gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1); osc.start(); osc.stop(audioContext.currentTime + 0.1); } // Handle image upload document.getElementById('carImage').addEventListener('change', function(e) { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = function(event) { customCarImage = new Image(); customCarImage.onload = function() { imageLoaded = true; }; customCarImage.src = event.target.result; }; reader.readAsDataURL(file); } }); // Input handling document.addEventListener('keydown', (e) => { if (!gameRunning) return; if (e.key === 'ArrowLeft' && currentLane > 0) { currentLane--; player.targetX = lanes[currentLane]; player.tilt = -0.3; playWhoosh(); } if (e.key === 'ArrowRight' && currentLane < 2) { currentLane++; player.targetX = lanes[currentLane]; player.tilt = 0.3; playWhoosh(); } }); // Touch controls let touchStartX = 0; canvas.addEventListener('touchstart', (e) => { touchStartX = e.touches[0].clientX; }); canvas.addEventListener('touchend', (e) => { if (!gameRunning) return; const touchEndX = e.changedTouches[0].clientX; const diff = touchStartX - touchEndX; if (Math.abs(diff) > 30) { if (diff > 0 && currentLane > 0) { currentLane--; player.targetX = lanes[currentLane]; player.tilt = -0.3; playWhoosh(); } else if (diff < 0 && currentLane < 2) { currentLane++; player.targetX = lanes[currentLane]; player.tilt = 0.3; playWhoosh(); } } }); function startGame() { if (audioContext.state === 'suspended') { audioContext.resume(); } startEngineSound(); document.getElementById('startScreen').style.display = 'none'; document.getElementById('highScore').textContent = highScore; gameRunning = true; resetVariables(); gameLoop(); } function resetVariables() { score = 0; speed = 0; currentLane = 1; player.x = lanes[1]; player.targetX = lanes[1]; player.tilt = 0; enemies = []; frame = 0; } function spawnEnemy() { const lane = Math.floor(Math.random() * 3); const types = ['sedan', 'truck', 'sport']; const type = types[Math.floor(Math.random() * types.length)]; const enemy = { x: lanes[lane], y: -150, lane: lane, width: type === 'truck' ? 60 : 50, height: type === 'truck' ? 100 : (type === 'sport' ? 70 : 80), type: type, speed: 3 + Math.random() * 2 + (speed * 0.1), color: type === 'sedan' ? '#ff006e' : (type === 'truck' ? '#fb5607' : '#00d9ff') }; // Don't spawn on top of another enemy const tooClose = enemies.some(e => e.lane === lane && e.y < 100); if (!tooClose) { enemies.push(enemy); } } function update() { frame++; // Accelerate if (speed < maxSpeed) { speed += 0.02; } // Update score score += Math.floor(speed); document.getElementById('score').textContent = Math.floor(score); document.getElementById('speed').textContent = Math.floor(speed * 20); // Update engine sound updateEngineSound(); // Smooth lane change player.x += (player.targetX - player.x) * 0.15; // Return tilt to center player.tilt *= 0.9; // Road scrolling roadOffset += speed; if (roadOffset > 80) roadOffset = 0; // Spawn enemies const spawnRate = Math.max(30, 100 - Math.floor(speed * 3)); if (frame % spawnRate === 0) { spawnEnemy(); } // Update enemies for (let i = enemies.length - 1; i >= 0; i--) { let enemy = enemies[i]; enemy.y += enemy.speed + speed; // Collision detection (pixel perfect-ish) if (Math.abs(player.x - enemy.x) < 35 && Math.abs(player.y - enemy.y) < (player.height + enemy.height) / 2 - 10) { playCrashSound(); gameOver(); return; } // Remove if off screen if (enemy.y > canvas.height + 100) { enemies.splice(i, 1); score += 100; // Bonus for passing } } } function draw() { // Clear ctx.fillStyle = '#0a0a0e'; ctx.fillRect(0, 0, canvas.width, canvas.height); // Draw road ctx.fillStyle = '#151520'; ctx.fillRect(20, 0, 360, canvas.height); // Road borders (neon) ctx.shadowBlur = 20; ctx.shadowColor = '#00d9ff'; ctx.strokeStyle = '#00d9ff'; ctx.lineWidth = 3; ctx.beginPath(); ctx.moveTo(20, 0); ctx.lineTo(20, canvas.height); ctx.moveTo(380, 0); ctx.lineTo(380, canvas.height); ctx.stroke(); ctx.shadowBlur = 0; // Lane markers (moving) ctx.strokeStyle = 'rgba(255,255,255,0.3)'; ctx.lineWidth = 2; ctx.setLineDash([40, 40]); ctx.lineDashOffset = -roadOffset; for (let i = 1; i < 3; i++) { ctx.beginPath(); ctx.moveTo(20 + (360/3) * i, 0); ctx.lineTo(20 + (360/3) * i, canvas.height); ctx.stroke(); } ctx.setLineDash([]); // Draw enemies for (let enemy of enemies) { ctx.save(); ctx.translate(enemy.x, enemy.y); // Shadow ctx.fillStyle = 'rgba(0,0,0,0.5)'; ctx.fillRect(-enemy.width/2 + 5, enemy.height/2 - 5, enemy.width, 10); // Car body glow ctx.shadowBlur = 15; ctx.shadowColor = enemy.color; // Main body ctx.fillStyle = enemy.color; ctx.fillRect(-enemy.width/2, -enemy.height/2, enemy.width, enemy.height); // Windshield ctx.fillStyle = '#1a1a2e'; ctx.fillRect(-enemy.width/2 + 5, -enemy.height/2 + 10, enemy.width - 10, 20); // Taillights (glowing red) ctx.shadowBlur = 20; ctx.shadowColor = '#ff0000'; ctx.fillStyle = '#ff0000'; ctx.fillRect(-enemy.width/2 + 2, enemy.height/2 - 5, 12, 5); ctx.fillRect(enemy.width/2 - 14, enemy.height/2 - 5, 12, 5); // Headlights (facing down since they're coming at us) ctx.shadowBlur = 30; ctx.shadowColor = '#ffffaa'; ctx.fillStyle = '#ffffaa'; ctx.fillRect(-enemy.width/2 + 5, -enemy.height/2 + 2, 10, 5); ctx.fillRect(enemy.width/2 - 15, -enemy.height/2 + 2, 10, 5); ctx.restore(); } // Draw player ctx.save(); ctx.translate(player.x, player.y); ctx.rotate(player.tilt); // Shadow ctx.fillStyle = 'rgba(0,0,0,0.6)'; ctx.fillRect(-25, 35, 50, 15); if (imageLoaded && customCarImage) { // Custom car image ctx.drawImage(customCarImage, -25, -40, 50, 80); } else { // Default neon car ctx.shadowBlur = 25; ctx.shadowColor = '#00d9ff'; // Body ctx.fillStyle = '#00d9ff'; ctx.fillRect(-25, -40, 50, 80); // Stripe ctx.fillStyle = '#fff'; ctx.fillRect(-5, -40, 10, 80); // Windshield ctx.fillStyle = '#0a0a1a'; ctx.fillRect(-20, -30, 40, 25); // Headlights (strong beams) ctx.shadowBlur = 40; ctx.shadowColor = '#ffffff'; ctx.fillStyle = '#ffffff'; ctx.fillRect(-20, -42, 15, 8); ctx.fillRect(5, -42, 15, 8); // Headlight beams ctx.globalAlpha = 0.3; ctx.fillStyle = '#ffffff'; ctx.beginPath(); ctx.moveTo(-15, -42); ctx.lineTo(-40, -200); ctx.lineTo(-5, -200); ctx.fill(); ctx.beginPath(); ctx.moveTo(15, -42); ctx.lineTo(40, -200); ctx.lineTo(5, -200); ctx.fill(); ctx.globalAlpha = 1; // Taillights ctx.shadowBlur = 20; ctx.shadowColor = '#ff006e'; ctx.fillStyle = '#ff006e'; ctx.fillRect(-20, 35, 15, 5); ctx.fillRect(5, 35, 15, 5); } ctx.restore(); // Speed lines (when fast) if (speed > 10) { ctx.strokeStyle = 'rgba(255,255,255,0.1)'; ctx.lineWidth = 2; for (let i = 0; i < 3; i++) { let x = Math.random() * canvas.width; ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x - (Math.random() - 0.5) * 50, canvas.height); ctx.stroke(); } } } function gameOver() { gameRunning = false; if (score > highScore) { highScore = Math.floor(score); localStorage.setItem('neonRacerHigh', highScore); } document.getElementById('finalScore').textContent = Math.floor(score); document.getElementById('finalHighScore').textContent = highScore; document.getElementById('gameOver').style.display = 'block'; } function resetGame() { document.getElementById('gameOver').style.display = 'none'; startEngineSound(); gameRunning = true; resetVariables(); gameLoop(); } function gameLoop() { if (!gameRunning) return; update(); draw(); requestAnimationFrame(gameLoop); } // Initial draw draw();